#include "winghexasm.h"
#include <QApplication>
#include <QHBoxLayout>
#include <QLabel>
#include <QMessageBox>
#include <QMetaEnum>
#include <QPushButton>
#include <QSettings>
#include <QVBoxLayout>

#define ICONRES(name) QIcon(":/WingHexAsm/img/" name ".png")
#define HOSTICONRES(name) QIcon(HOSTRESPIMG(name))

WingHexAsm::WingHexAsm(QObject *parent) { Q_UNUSED(parent) }

bool WingHexAsm::init(QList<WingPluginInfo> loadedplugin) {
  Q_UNUSED(loadedplugin);

  if (SDKVERSION < 8) {
    QMessageBox::critical(nullptr, "Error",
                          "UnSupported Plugin System Version!",
                          QMessageBox::Ok);
    return false;
  }

  auto translator = new QTranslator(this);

  auto s = GETPLUGINQM("WingHexAsm.qm");
  if (!translator->load(s) || !QApplication::installTranslator(translator)) {
    QMessageBox::critical(nullptr, "Error", "Error Loading Translation File!",
                          QMessageBox::Ok);
    return false;
  }

  w = new QWidget;
  auto vlayout = new QVBoxLayout(w);
  auto hlayout = new QHBoxLayout;

  hlayout->addWidget(new QLabel(tr("Arch :"), w));
  hlayout->addSpacing(10);

  // 默认 x86_64 64 位指令
  arch = new QComboBox(w);
  QStringList archs(
      {"ARM", "ARM64", "MIPS", "X86_64", "PPC", "SPARC", "EVM", "MOS65XX"});
  arch->addItems(archs);
  arch->setCurrentIndex(3);

  void (QComboBox::*indexChanged)(int index) = &QComboBox::currentIndexChanged;
  connect(arch, indexChanged, [=](int index) {
    switch (index) {
    case 0:
      karch = ks_arch::KS_ARCH_ARM;
      break;
    case 1:
      karch = ks_arch::KS_ARCH_ARM64;
      break;
    case 2:
      karch = ks_arch::KS_ARCH_MIPS;
      break;
    case 3:
      karch = ks_arch::KS_ARCH_X86;
      break;
    case 4:
      karch = ks_arch::KS_ARCH_PPC;
      break;
    case 5:
      karch = ks_arch::KS_ARCH_SPARC;
      break;
    case 6:
      karch = ks_arch::KS_ARCH_EVM;
      break;
    }
  });

  hlayout->addWidget(arch);
  hlayout->addSpacing(30);

  hlayout->addWidget(new QLabel(tr("Mode :"), w));
  hlayout->addSpacing(10);

  mode = new QComboBox(w);
  QStringList modes({"x16", "x32", "x64", "THUMB", "MIPS32", "MIPS64"});
  mode->addItems(modes);
  mode->setCurrentIndex(2);

  connect(mode, indexChanged, [=](int index) {
    switch (index) {
    case 0:
      kmode = ks_mode::KS_MODE_16;
      break;
    case 1:
      kmode = ks_mode::KS_MODE_32;
      break;
    case 2:
      kmode = ks_mode::KS_MODE_64;
      break;
    case 3:
      kmode = ks_mode::KS_MODE_THUMB;
      break;
    case 4:
      kmode = ks_mode::KS_MODE_MIPS32;
      break;
    case 5:
      kmode = ks_mode::KS_MODE_MIPS64;
      break;
    }
  });

  hlayout->addWidget(mode);
  hlayout->addSpacing(10);

  cbintel = new QCheckBox("Intel", w);
  cbintel->setChecked(true);
  hlayout->addWidget(cbintel);

  hlayout->addStretch();

  vlayout->addSpacing(10);

  txtAsm = new QTextBrowser(w);
  txtAsm->setReadOnly(false);
  vlayout->addItem(hlayout);
  vlayout->addWidget(txtAsm);

  vlayout->addSpacing(10);
  auto asmbtn = new QPushButton(tr("AsmCode"), w);
  connect(asmbtn, &QPushButton::clicked, this, [=] {
    auto txt = txtAsm->toPlainText();
    if (txt.length()) {
      this->asmCode(txt);
    } else {
      this->toast(ICONRES("icon"), tr("No Asm"));
    }
  });
  vlayout->addWidget(asmbtn);

  PluginDockWidgetInit(dw, w, tr("AsmWindow"), "AsmWindow");

  PluginMenuInitBegin(menu, tr("WingHexAsm")) {
    menu->setIcon(ICONRES("icon"));
    PluginMenuAddItemIconLamba(menu, tr("AsmWindow"), ICONRES("icon"), [=] {
      dw->show();
      dw->raise();
    });
    PluginMenuAddItemIconLamba(menu, tr("Author"), HOSTICONRES("author"), [=] {
      auto authord =
          newAboutDialog(QPixmap(), {":/WingHexAsm", ":/WingHexAsm/img"});
      authord->exec();
      delete authord;
    });
    PluginMenuAddItemIconLamba(menu, tr("Sponsor"), HOSTICONRES("sponsor"),
                               [=] {
                                 auto sponsor = newSponsorDialog();
                                 sponsor->exec();
                                 delete sponsor;
                               });
  }
  PluginMenuInitEnd();

  tbtn = new QToolButton;
  tbtn->setIcon(ICONRES("icon"));

  connect(tbtn, &QToolButton::clicked, this, [=] {
    dw->show();
    dw->raise();
  });

  return true;
}

WingHexAsm::~WingHexAsm() {}

void WingHexAsm::unload() {}

int WingHexAsm::sdkVersion() { return SDKVERSION; }

QMenu *WingHexAsm::registerMenu() { return menu; }

QToolButton *WingHexAsm::registerToolButton() { return tbtn; }

void WingHexAsm::registerDockWidget(
    QHash<QDockWidget *, Qt::DockWidgetArea> &rdw) {
  rdw.insert(dw, Qt::DockWidgetArea::BottomDockWidgetArea);
}

const QString WingHexAsm::pluginName() { return tr("WingHexAsm"); }

const QString WingHexAsm::pluginAuthor() { return WINGSUMMER; }

uint WingHexAsm::pluginVersion() { return 1; }

const QString WingHexAsm::signature() { return WINGSUMMER; }

const QString WingHexAsm::pluginComment() {
  return tr("A small assembly plugin for WingHexExplorer.");
}

void WingHexAsm::plugin2MessagePipe(WingPluginMessage type,
                                    QList<QVariant> msg) {
  Q_UNUSED(type);
  Q_UNUSED(msg);
}

void WingHexAsm::asmCode(QString code) {
  ks_engine *ks;
  ks_err err;
  size_t count;
  unsigned char *encode;
  size_t size;

  QIcon icon = ICONRES("icon");

  err = ks_open(karch, kmode, &ks);
  if (err != KS_ERR_OK) {
    toast(icon, tr("ERROR: failed on ks_open()"));
    return;
  }

  if (cbintel->isChecked()) {
    ks_option(ks, KS_OPT_SYNTAX, KS_OPT_SYNTAX_INTEL);
  } else {
    ks_option(ks, KS_OPT_SYNTAX, KS_OPT_SYNTAX_ATT);
  }

  if (ks_asm(ks, code.toUtf8().constData(), 0, &encode, &size, &count)) {
    toast(icon, tr("ERROR: failed on ks_asm() with count = %1, error code = %2")
                    .arg(count)
                    .arg(ks_errno(ks)));
  } else {
    QByteArray buffer(reinterpret_cast<char *>(encode), int(size));
    if (reader.currentDoc() < 0) {
      USINGCONTROL({
        controller.newFile();
        controller.insert(0, buffer); // 直接使用 insert 会比 append 更高效一点
        controller.moveTo(buffer.size());
      });
    } else {
      USINGCONTROL({
        auto pos = reader.currentOffset();
        controller.insert(qint64(pos), buffer);
        controller.moveTo(qint64(pos + ulong(buffer.size())));
      });
    }
    toast(icon, tr("ASMSuccessfully"));
  }

  // NOTE: free encode after usage to avoid leaking memory
  ks_free(encode);

  // close Keystone instance when done
  ks_close(ks);
}

#if QT_VERSION < 0x050000
Q_EXPORT_PLUGIN2(WingHexAsm, WingHexAsm)
#endif // QT_VERSION < 0x050000
